package org.fixies.utils;

import junit.framework.AssertionFailedError;
import org.apache.commons.beanutils.BeanMap;

import java.util.Map;

import static org.fixies.utils.StringUtils.concat;


public class Assert {

    /**
     * Beanの比較を行う。
     *
     * @param expected 期待する値
     * @param actual   実際の値
     */
    public static void assertBeanEquals(Object expected, Object actual) {
        assertBeanEquals("", expected, actual);
    }

    /**
     * Beanの比較を行う。
     *
     * @param msg      メッセージ
     * @param expected 期待する値
     * @param actual   実際の値
     */
    public static void assertBeanEquals(String msg, Object expected, Object actual) {

        if (expected == null && actual == null) return;
        checkNull(msg, expected, actual);

        Class<?> expClass = expected.getClass();
        Class<?> actClass = actual.getClass();
        if (!expClass.isAssignableFrom(actClass) && !actClass.isAssignableFrom(expClass)) {
            // クラスがちがう
            msg = msg + " expected is [" + expClass.getName() + "], but was [" + actClass.getName() + "]";
            throw new AssertionFailedError(msg);
        }
        Map<?, ?> a = new BeanMap(actual);
        Map<?, ?> e = new BeanMap(expected);
        org.junit.Assert.assertEquals(msg, StringUtils.convert(e), StringUtils.convert(a));
    }

    private static void checkNull(String msg, Object expected, Object actual)
            throws AssertionFailedError {

        // 両方null
        if (expected == null && actual == null) {
            return;
        }
        // 期待値のみnull
        if (expected == null) {
            String m = concat(msg,
                    " expected is null, but was [", actual, "]");
            throw new AssertionFailedError(m);
        }

        // 実際の値がnull
        if (actual == null) {
            String m = concat(msg, " expected is [", expected, "], but was null");
            throw new AssertionFailedError(m);
        }
    }

    public static void assertEqualsAsString(Object expected, Object actual) {
        assertEqualsAsString("", expected, actual);
    }

    public static void assertEqualsAsString(String msg, Object expected, Object actual) {

        String expectedString = StringUtils.convert(expected);
        String actualString = StringUtils.convert(actual);
        org.junit.Assert.assertEquals(msg, expectedString, actualString);
    }
}
